//
//  CZRefreshControl.swift
//  weibo
//
//  Created by Summer on 2017/12/14.
//  Copyright © 2017年 Summer. All rights reserved.
//

import UIKit

/// 刷新控件切换状态临界点
/// 普通刷新高度 60；动画刷新高度 120
private let CZRefreshOffset: CGFloat = 120

/// 刷新状态
///
/// - normal: 普通状态，没有刷新
/// - pulling: 超过临界点，如果放手，开始刷新
/// - willRefresh: 用户超过临界点，并且放手
enum CZRefreshState {
    case normal
    case pulling
    case willRefresh
}

/// 刷新控件
class CZRefreshControl: UIControl {

    /// 使用UIScrollView可以让tableView和collectionView都能使用
    private weak var scrollView: UIScrollView?
    
    private lazy var refreshView: CZRefreshView = CZRefreshView.refreshView()
    
    // MARK: 构造函数
    init() {
        super.init(frame: CGRect())
        setupUI()
    }
    // XIB
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupUI()
    }
    
    /*
     willMove 方法会调用 addSubview
     - 当添加到父视图的时候，newSuperview是父视图
     - 当父视图被移除，newSuperview为nil
     */
    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        
        guard let sv = newSuperview as? UIScrollView else {
            return
        }

        scrollView = sv

        // KVO监听父视图的contentOffset属性
        scrollView?.addObserver(self, forKeyPath: "contentOffset", options: [], context: nil)
    }
    
    // 移除 KVO监听
    // 所有的下拉刷新都是监听父视图的contentOffset
    override func removeFromSuperview() {
        
        // superview还存在
        superview?.removeObserver(self, forKeyPath: "contentOffset")
        
        super.removeFromSuperview()
        
        // superview不存在
    }
    
    // 注意：通知中心，如果不释放，什么也不会发生，但是会内存泄漏，会有多次注册的可能
    // KVO，如果不释放，会崩溃
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        // contentOffset 的 Y 值跟 contentInset 的 top 有关
        // print(scrollView?.contentOffset)
        
        guard let sv = scrollView else {
            return
        }
        
        // 刷新控件初始高度应该为0
        var height = -(sv.contentInset.top + sv.contentOffset.y)
        if #available(iOS 11.0, *) {
            height -= 64
        }
        // print(height)
        refreshView.viewHeight = height
        
        // 视图往上拖动，直接返回
        if height < 0 {
            return
        }
        
        // 可以根据高度height设置刷新控件的frame
        // sv的frame的原点为向下拉表格的位置
        // frame本身的值不会变，contentOffset的y值在改变
        self.frame = CGRect(x: 0,
                            y: -height,
                            width: sv.bounds.width,
                            height: height)
        
        // 判断临界点 - 只需要判断一次
        if sv.isDragging {
            
            if height > CZRefreshOffset && (refreshView.refreshState == .normal) {
                refreshView.refreshState = .pulling
                print("放手刷新。。")
            } else if height <= CZRefreshOffset && (refreshView.refreshState == .pulling) {
                refreshView.refreshState = .normal
                print("再使劲儿。。")
            }
            
        } else {
            // 放手，判断是否超过临界点
            if refreshView.refreshState == .pulling {
                print("准备刷新。。")
                
                beginRefreshing()
                
                // 发送刷新数据事件
                sendActions(for: .valueChanged)
            }
        }
    }
    
    func beginRefreshing() {
        print("开始刷新")
        
        // 判断父视图
        guard let sv = scrollView else {
            return
        }
        
        // 判断刷新状态，正在刷新直接返回
        if refreshView.refreshState == .willRefresh {
            return
        }
        
        // 设置刷新视图状态
        refreshView.refreshState = .willRefresh
        // 调整表格间距
        var inset = sv.contentInset
        inset.top += CZRefreshOffset
        sv.contentInset = inset
        
        // 设置刷新视图的父视图高度
        refreshView.viewHeight = CZRefreshOffset
    }
    
    func endRefreshing() {
        
        // 判断父视图
        guard let sv = scrollView else {
            return
        }
        
        // 判断刷新状态，如果不是，直接返回
        if refreshView.refreshState != .willRefresh {
            return
        }
        // 恢复刷新视图状态
        refreshView.refreshState = .normal
        
        // 恢复表格间距
        var inset = sv.contentInset
        inset.top -= CZRefreshOffset
        sv.contentInset = inset
    }
}

extension CZRefreshControl {
    private func setupUI() {
        
        backgroundColor = superview?.backgroundColor
        
        // 添加刷新视图 - 从XIB中加载出来，默认是XIB中指定的宽高
        addSubview(refreshView)
        
        // 自动布局 - 设置XIB的自动布局，需要指定宽高约束
        refreshView.translatesAutoresizingMaskIntoConstraints = false
        
        addConstraint(NSLayoutConstraint(item: refreshView,
                                         attribute: .centerX,
                                         relatedBy: .equal,
                                         toItem: self,
                                         attribute: .centerX,
                                         multiplier: 1.0,
                                         constant: 0))
        
        addConstraint(NSLayoutConstraint(item: refreshView,
                                         attribute: .bottom,
                                         relatedBy: .equal,
                                         toItem: self,
                                         attribute: .bottom,
                                         multiplier: 1.0,
                                         constant: 0))
        
        addConstraint(NSLayoutConstraint(item: refreshView,
                                         attribute: .width,
                                         relatedBy: .equal,
                                         toItem: nil,
                                         attribute: .notAnAttribute,
                                         multiplier: 1.0,
                                         constant: refreshView.bounds.width))
        
        addConstraint(NSLayoutConstraint(item: refreshView,
                                         attribute: .height,
                                         relatedBy: .equal,
                                         toItem: nil,
                                         attribute: .notAnAttribute,
                                         multiplier: 1.0,
                                         constant: refreshView.bounds.height))
    }
}
